import request from "../util/downloadRequire";
// @ts-ignore
import { ElMessage } from "element-plus";
import { saveFile } from "../util/utils";
import { Base64 } from "js-base64";
import { FileItf, OnListener } from "@/interface";
import axios from "axios";

/**
 * @description: 普通下载
 * @param {string} path
 * @return {*}
 * @author: tao wenbo 2419421809@qq.com
 */
export function download(path: string) {
    if (path.trim() === "") {
        ElMessage.error("路径出错");
        return;
    }
    request({
        method: "GET",
        url: "/api/download",
        // 设置 responseType 为 'blob'，表示响应结果是二进制数据
        responseType: "blob",
        params: {
            path,
        },
    })
        .then((res) => {
            const content = res.data;
            const blob = new Blob([content]);
            const fileName = res.headers["content-disposition"].split("filename=")[1];
            if ("download" in document.createElement("a")) {
                // 非IE下载
                const elink = document.createElement("a");
                elink.download = fileName;
                elink.style.display = "none";
                elink.href = window.URL.createObjectURL(blob);
                document.body.appendChild(elink);
                elink.click();
                window.URL.revokeObjectURL(elink.href); // 释放URL 对象
                document.body.removeChild(elink);
            } else {
                // IE10+下载
                (navigator as any).msSaveBlob(blob, fileName);
            }
        })
        .catch((err) => {
            if (err) {
                ElMessage.error(err.message);
            } else {
                ElMessage.warning("报错了");
            }
        });
}

/**
 * @description: 迅雷下载
 * @return {*}
 * @author: tao wenbo 2419421809@qq.com
 */
export function thunderDownload() {
    let thunderLink = `AAhttp://localhost:5009/api/downloadZZ`;
    console.log(thunderLink);
    const buf = btoa(thunderLink);
    // console.log(buf);
    return `thunder://${buf}`;
}

/**
 * @description: 获取大小和名字
 * @param {string} path
 * @return {*}
 * @author: tao wenbo 2419421809@qq.com
 */
export async function getFileSizeAndName(id: number, Authorization: string = localStorage.getItem("token") || "") {
    let res = await request.head("/api/download/range", {
        headers: {
            id,
            Authorization: Authorization,
            "Content-Type": "application/json;charset=UTF-8",
        },
    });
    return {
        size: Number(res.headers["content-length"]),
        filename: Base64.decode(res.headers["filename"]),
    };
}

export class RangeDownload {
    public blob: Blob[] = [];
    public listenInArr: number[] = [];
    public isDownloading: boolean = false;
    public isFinish: boolean = false;
    private listenInArrProxy: number[] = [];
    private startTime: number = 0;
    private endTime: number = 0
    // 取消axios的方法
    private cancelTokenSource = axios.CancelToken.source();
    // 定义分段大小
    public CHUNK_SIZE = 256 * 1024; // 256kB
    constructor(private file: FileItf, private onListen?: OnListener, private authorization: string = localStorage.getItem("token") || "") {
        console.log("新建了RangeDownloadClass");
    }

    /**
     * @description: 分段下载的封装函数
     * @return {*}
     * @author: tao wenbo 2419421809@qq.com
     */
    async rangeDownLoadApi() {
        let { fileName } = this.file;
        this.isDownloading = true;
        this.onListen?.onStart && this.onListen?.onStart.call(this, fileName);
        let total = this.blob.reduce((a, b) => a + (b.size || 0), 0);
        this.onListen?.onProcess && this.onListen?.onProcess(total, this.file.size);
        try {
            let blob = await this.downloadFile();
            this.onListen?.onFinished && this.onListen?.onFinished.call(this, this.endTime - this.startTime, blob);
            this.isFinish = true;
            this.isDownloading = false;
            this.blob = []
            // 如果放入webwork中此处无效
            saveFile(blob, fileName);
            return this.blob;
        } catch (error) {
            return new Error("下载失败");
        }
    }

    /**
     * @description: 定义分段下载函数，参数分别为文件 URL 和总字节数
     * @return {*}
     * @author: tao wenbo 2419421809@qq.com
     */
    private async downloadFile() {
        // 开始时间，用于计算耗时
        this.startTime = new Date().getTime();
        // 分块
        let chunks = [];
        // 起始和结束下标
        let start = 0;
        let end = 0;

        // 将文件分段，并将分段信息保存到数组中
        while (end < this.file.size - 1) {
            end = start + this.CHUNK_SIZE - 1;
            chunks.push({ start, end });
            start = end + 1;
        }
        let chunkcopy = chunks
        // 只需要判断blob中有哪些，blob和chuans的下标肯定是一样的
        chunks = chunks.map((_, index) => {
            if (!this.blob[index]) {
                return _;
            }
            return undefined;
        });

        // 定义并发下载任务数
        const MAX_CONCURRENT_TASKS = 3;
        // 总共的分块数量
        const chunkCount = Math.ceil(this.file.size / this.CHUNK_SIZE);
        // 普通的数组对象,后期对其进行代理，监听变化
        this.listenInArr = new Array(chunkCount).fill(0);
        // 总共已经下载的数据
        let totalHasDownloaded = 0;
        let that = this;
        // 配置代理，用于计算已经下载好的数据量
        this.listenInArrProxy = new Proxy(this.listenInArr, {
            get: function (target, name: any) {
                return target[name];
            },
            set: function (target, name: any, value) {
                target[name] = value;
                totalHasDownloaded = that.listenInArrProxy.reduce((a, b) => (b == that.CHUNK_SIZE ? a : a + b), 0);
                // let total = that.blob.filter( item => item ).reduce( ( a, b ) => a + b.size, 0 )
                // that.onListen?.onProcess && that.onListen?.onProcess( total, that.file.size );
                return true;
            },
        });
        // 利用 Promise.all() 控制并发下载的分段数量
        const promises = [];
        let index = 0;
        for (let i = 0; i < chunks.length; i += MAX_CONCURRENT_TASKS) {
            const tasks = chunks.slice(i, i + MAX_CONCURRENT_TASKS);
            const chunkPromises = tasks.map((chunk) => {
                if (chunk) {
                    return this.downloadFileByChunk(chunk.start, chunk.end, index++);
                } else {
                    index++;
                }
            });
            promises.push(Promise.all(chunkPromises));
        }
        // 整合分段数据，返回完整的文件数据
        return Promise.all(promises).then(
            (res: any) => {
                this.endTime = new Date().getTime();
                const blob = new Blob(this.blob, { type: "application/octet-stream" });
                let url = URL.createObjectURL(blob);
                return url;
            },
            (err) => {
                this.isDownloading = false;
                if (axios.isCancel(err)) {
                    this.onListen?.onCanceled && this.onListen?.onCanceled.call(this, this.blob);
                } else {
                    this.onListen?.onFailed && this.onListen?.onFailed.call(this, err);
                }
                return Promise.reject(err);
            }
        );
    }
    /**
     * @param {number} id 资源ID
     * @param {number} start 开始位置
     * @param {number} end
     * @return {*}
     * @author: tao wenbo 2419421809@qq.com
     */
    private downloadFileByChunk(start: number, end: number, blobIndex: number): Promise<Blob> {
        return new Promise((resolve, reject) => {
            request
                .get("/api/download/range", {
                    cancelToken: this.cancelTokenSource.token,
                    headers: {
                        Range: `bytes=${start}-${end}`,
                        Authorization: this.authorization,
                    },
                    params: {
                        id: this.file.id,
                    },
                    responseType: "blob",
                    onDownloadProgress: (progressEvent) => {
                        this.listenInArrProxy[blobIndex] = progressEvent.loaded;
                    },
                })
                .then((res) => {
                    this.blob[blobIndex] = res.data;
                    let total = this.blob.reduce((a, b) => a + (b.size || 0), 0);
                    this.onListen?.onProcess && this.onListen?.onProcess(total, this.file.size);
                    resolve(res.data);
                })
                .catch((err) => {
                    reject(err);
                });
        });
    }

    public StopDownload() {
        // 只有当下载的时候才能暂停
        if (this.isDownloading) {
            this.cancelTokenSource.cancel();
            this.cancelTokenSource = axios.CancelToken.source();
            this.isDownloading = false;
            return true;
        }
        return false;
    }
}
